#ifndef __ILLUMINATION_ACTUATOR_H__
#define __ILLUMINATION_ACTUATOR_H__

#include <Time.h>
#include <Arduino.h>

#include "Actuator.h"
#include "Errors.h"

#define timeToSeconds(H, M, S) ((H*SECS_PER_HOUR) + (M*SECS_PER_MIN) + (S))

typedef unsigned long seconds_t; // number of seconds from midnight

const uint8_t MIN_LEVEL_VALUE = 0;
const uint8_t MAX_LEVEL_VALUE = 255;

enum Mode {
    AUTO, // Auto-mode. Depends on time of the day calculates illumination
    MANUAL, // Const pre-defined illumination
    LIGHTNING, // Lightning mode
    BLAZE, // Max illumination value when all sections
    DEMO // Demo mode. The same as auto but time 100 times faster
};

enum SearchOption {
    BEFORE, SAME, AFTER
};

class Levels {
private:
    uint8_t count;
    uint8_t *lines;

private:
    error_t checkLineNumber(uint8_t) const;

    error_t checkLevelValue(uint8_t) const;

public:
    Levels(uint8_t count);

    ~Levels();


    const uint8_t getSize() const;


    void setLevels(uint8_t level) const;

    void setLevels(Levels &level) const;

    void setLevels(uint8_t levels[]) const;


    const uint8_t getLevel(uint8_t line) const;

    void setLevel(uint8_t line, uint8_t level) const;


    void clear() const;
};

class TimePoint {
    friend class Illumination;

private:
    Levels *levels;
    seconds_t time;

    TimePoint *next;
    TimePoint *previous;

    TimePoint(seconds_t time, uint8_t levels);

    ~TimePoint();

public:
    const seconds_t getTime() const;

    const Levels *getLevels() const;


    const TimePoint *getNextPoint() const;

    const TimePoint *getPreviousPoint() const;
};

class Illumination : public Actuator, public CommandProcessor {
private:
    Mode mode;

    uint8_t *pins;
    uint8_t lines;
    uint8_t points;

    Levels *levels;

    TimePoint *head;
    TimePoint *tail;

    int previousSeconds;
    time_t lastUpdateTime;

private:
    void createTimePoint(uint8_t hour, uint8_t s1, uint8_t s2, uint8_t ill, uint8_t moon);

private:
    void validatePins();

    bool calculateLevel(time_t time, Levels *levels);

    TimePoint *findElement(seconds_t point, SearchOption option);

public:
    Illumination(uint8_t lines, const uint8_t pins[]);

    ~Illumination();


    const Mode getMode() const;

    void setMode(const Mode mode);


    const Levels *getLevels() const;

    const time_t getLastUpdateTime() const;


    const uint8_t getSize() const;

    const TimePoint *getFirstPoint() const;

    const TimePoint *getLastPoint() const;


    const Levels *createPoint(seconds_t time);

    void deletePoint(seconds_t time);

public: // Actuator
    virtual const error_t begin();

    virtual const error_t tick(const tick_t ticks, const time_t time);

public: // CommandProcessor
    virtual const bool isSupported(String const &request);

    virtual const error_t processCommand(String const &request, String &response);
};

#endif
